#ifndef __SS_H__
#define __SS_H__
#include"db.hpp"
#include"online.hpp"

typedef enum {UNLOGIN,LOGIN}ss_statu;
class session
{
public:
    session(uint64_t ssid):_ssid(ssid)//需要在session管理模块给他分配ssid,所以需要传进来 
    {DLOG("SESSION %p 被创建",this);}
    ~session(){DLOG("SESSION %p 被销毁",this);}
    void set_status(ss_statu status){_status=status;}
    void set_user(uint64_t uid){  _uid=uid; }
    uint64_t get_user(){return _uid;}
    bool is_login(){return (_status==LOGIN);}
    void set_timer(const wsserver_t::timer_ptr& tp){_tp=tp;}
    wsserver_t::timer_ptr get_timer(){return _tp;}
    uint64_t get_ssid(){return _ssid;}

private:
    uint64_t _ssid;
    uint64_t _uid;//session里保存的是哪一个用户的数据
    ss_statu _status;//用户状态：未登录，已登陆
    wsserver_t::timer_ptr _tp;//session关联的定时器
};

#define SESSION_TIMEOUT 30000
#define SESSION_TIMEFOR -1

using session_ptr=std::shared_ptr<session>;
class session_manager
{
public:
    session_manager(wsserver_t* server):_server(server),_ssid_allocte(1)
    {
        DLOG("session管理器创建成功");
    }
    ~session_manager(){
        DLOG("session管理器即将销毁");
    }
    session_ptr session_create(uint64_t uid,ss_statu status){
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_ssid_allocte));
        ssp->set_status(status);
        ssp->set_user(uid);
        _sessions.insert(std::make_pair(_ssid_allocte,ssp));
        _ssid_allocte++;
        return ssp;
    }
    session_ptr get_session(uint64_t ssid){
        std::unique_lock<std::mutex> lock(_mutex);
        auto it =_sessions.find(ssid);
        if(it==_sessions.end()) return session_ptr();
        return it->second;
    }
    void remove_session(uint64_t ssid){
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
        // DLOG("定时时间到，session%ld已经被删除",ssid);//由于重置定时器时间，也会移除一次，这个日志消息无效，故取消
    }
    void append_session(const session_ptr& ssp){
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(ssp->get_ssid(),ssp));
    }
    /*设计思路*/
    // 登陆之后，创建session，session需要在指定时间无通信后删除
    //但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
    //等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
    void set_session_expire_time(uint64_t ssid,int ms=SESSION_TIMEFOR){
        //首先获取定时器
        session_ptr ssp=get_session(ssid);
        if(ssp.get()==nullptr) return;
        wsserver_t::timer_ptr tp=ssp->get_timer();
        if(tp==nullptr&&ms==SESSION_TIMEFOR) return ;
        else if(tp==nullptr&&ms!=SESSION_TIMEFOR){
            //tp为空说明，session没有关联定时器，所以为永久session，但是现在要让他不是永久，就要给他设置一个定时器，所以要用_server来获取设置一个定时器
            wsserver_t::timer_ptr temp_tp =_server->set_timer(ms,
                std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(temp_tp);
        }
        else if(tp!=nullptr&&ms==SESSION_TIMEFOR){
            //删除掉定时器，让它永久，但是定时器的取消，会导致删除session任务立即被执行，所以要重新创建一个一样的session
            tp->cancel();//tips:这个取消定时任务并不是立即被执行的，并不是立即取消的--->等到服务器内部感受到了才取消的？？？---->所以可能在重新添加后再执行，那重新添加的就又没了
            //所以添加操作，不能立即执行
            
            // _sessions.insert(std::make_pair(ssid,ssp));//不就是通过ssid来找到某个session的吗，为什么不能直接用？还要用智能指针来拿，如果被移除了，它还存在吗？
            //-->它还在，因为在前面用get_session获取到了智能指针，此时有两个引用计数指向同一块空间，当移除的时候，只减了引用计数。但是如果外界没有保存，移除了确实就没了
            ssp->set_timer(wsserver_t::timer_ptr());//设置一个空的timer
            // _sessions.insert(std::make_pair(ssp->get_ssid(),ssp));--->改成下面这种写法(使用定时器来添加)
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
        }
        else if(tp!=nullptr&&ms!=SESSION_TIMEFOR){
            tp->cancel();
            // ssp->set_timer(wsserver_t::timer_ptr());//感觉要不要都无所谓，反正后面都会再设置
            _server->set_timer(0,std::bind(&session_manager::append_session,this,ssp));
            wsserver_t::timer_ptr temp_tp =_server->set_timer(ms,
                std::bind(&session_manager::remove_session,this,ssid));
            ssp->set_timer(temp_tp);
        }

    }

private:
    uint64_t _ssid_allocte;
    std::mutex _mutex;
    std::unordered_map<uint64_t,session_ptr> _sessions;
    wsserver_t* _server;//用来获取定时器
};
#endif 